/* $Id: AbstractBigtableModelCore.java 135 2011-08-27 18:48:42Z altdotua@gmail.com $ */

package alt.djudge.frontend.server.models;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import org.apache.log4j.Logger;
import org.hibernate.criterion.Order;

import alt.djudge.frontend.server.PMF;
import alt.djudge.frontend.server.datatypes.AbstractEntry;


public abstract class AbstractBigtableModelCore<T extends AbstractEntry> implements CoreInterface<T>
{
	private static final Logger log = Logger.getLogger(AbstractBigtableModelCore.class);
	
	public T getEntry(Long entryId)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		T detachedCopy = null;
		try
		{
			tx.begin();
			T entry = (T) pm.getObjectById(getEntryClass(), entryId);
			tx.commit();
	        detachedCopy = pm.detachCopy(entry);
		}
		catch (Exception e)
		{
			log.info(e);
		}
		finally
		{
			if (tx.isActive())
				tx.rollback();
			pm.close();
		}
		return detachedCopy;
	}

	@Override
	public boolean deleteEntry(Long entryId)
	{
		AbstractEntry entry = getEntry(entryId);
		if (entry != null)
			return entry.delete();
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAllEntries()
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		pm.setDetachAllOnCommit(true);
		List<T> detachedList = new ArrayList<T>();
		Transaction tx = pm.currentTransaction();
		try
		{
			Query query = pm.newQuery(getEntryClass());
			query.setOrdering(getOrdering().toString());
			tx.begin();
			List<T> list = (List<T>) query.execute();
	        for(T obj : list)
	        {
	        	detachedList.add(pm.detachCopy(obj));
	        }
			tx.commit();
		}
		catch (Exception e)
		{
			log.info(e);
		}
		finally
		{
			if (tx.isActive())
			{
				tx.rollback();
			}
			pm.close();
		}
		return detachedList;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getEntries(Map<String, Object> map)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
//		pm.setDetachAllOnCommit(true);
		Transaction tx = pm.currentTransaction();
		List<T> detachedList = new Vector<T>();
		try
		{
			Map<String, Object> paramValues = new HashMap<String, Object>();
    		Vector<String> filterStrings = new Vector<String>();
    		Vector<String> filterParams = new Vector<String>();
    		
    		for (Object key: map.keySet()) if (!key.equals("page"))
    		{
    			String fieldName = key.toString();
    			System.out.println(fieldName + " => " + map.get(key).toString());
    			String keyName = key.toString() + "_key";
    			filterParams.add(getEntryClass().getDeclaredField(fieldName).getType().getSimpleName()+ " " + keyName);
    			filterStrings.add(fieldName + " == " + keyName);
    			paramValues.put(keyName, map.get(key));
    		}
    		
    		Query query = pm.newQuery(getEntryClass());

    		if (filterStrings.size() > 0)
    		{
    			StringBuilder sb = new StringBuilder();
    			StringBuilder sb2 = new StringBuilder();
    			sb.append(filterStrings.get(0));
    			sb2.append(filterParams.get(0));
    			for (int i = 1; i < filterStrings.size(); i++)
    			{
    				sb.append(" && ");
    				sb.append(filterStrings.get(i));
    				sb2.append(", ");
    				sb2.append(filterParams.get(i));
    			}
    			query.setFilter(sb.toString());
    			query.declareParameters(sb2.toString());
    		}
    		Integer page = -1;

    		try
    		{
	    		String fromStr = map.get("page").toString();
	    		page = Integer.parseInt(fromStr);
    		}
    		catch (Exception e) {}
    		
    		if (page != -1)
    		{
    			query.setRange(page * getPageSize(), (page+1) * getPageSize());
    		}
    		query.setOrdering(getOrdering().toString());
    		
    		tx.begin();
    		List<T> list = (List<T>) query.executeWithMap(paramValues);
			tx.commit();
	        for(T obj : list)
	        {
	        	detachedList.add(pm.detachCopy(obj));
	        }
		}
		catch (Exception e)
		{
			log.info(e);
		}
		finally
		{
			if (tx.isActive())
				tx.rollback();
			pm.close();
		}
		return detachedList;
	}
	
	protected abstract Class<T> getEntryClass();
	
	protected abstract Order getOrdering();
	
	protected Integer getPageSize()
	{
		return 100;
	}
	
	public void deleteAllEntries()
	{
		List<T> list = (List<T>) getAllEntries();
		for (T entry : list)
			entry.delete();
	}
	
	public T newEntry()
	{
		try
		{
			return getEntryClass().newInstance();
		}
		catch (Exception e)
		{
			log.fatal("Cannot create instance of " + getEntryClass().getSimpleName(), e);
		}
		return null;
	}
	
	@Override
	public CoreInterface<T> getAdapter()
	{
		return new CoreInterface<T>()
		{
			
			@Override
			public T getEntry(Long entryId)
			{
				return AbstractBigtableModelCore.this.getEntry(entryId);
			}
			
			@Override
			public List<T> getEntries(Map<String, Object> map)
			{
				return AbstractBigtableModelCore.this.getEntries(map);
			}
			
			@Override
			public List<T> getAllEntries()
			{
				return AbstractBigtableModelCore.this.getAllEntries();
			}
			
			@Override
			public boolean deleteEntry(Long entryId)
			{
				return AbstractBigtableModelCore.this.deleteEntry(entryId);
			}
			
			@Override
			public void deleteAllEntries()
			{
				AbstractBigtableModelCore.this.deleteAllEntries();
			}

			@Override
			public CoreInterface<T> getAdapter()
			{
				return AbstractBigtableModelCore.this;
			}

			@Override
			public T newEntry()
			{
				return AbstractBigtableModelCore.this.newEntry();
			}
		};
	}
}
